using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;

namespace NodeEditor {

    public class Pipe : IDisposable {

        public static Pipe EditingPipe;

        private PathFigure pathFigure;
        private BezierSegment bezier;

        private Path path;
        private Path hitTestPath;

        public NodeGraph graph => (inputDock != null) ? inputDock.node.graph : outputDock.node.graph;


        private InputDock _inputDock;
        public InputDock inputDock {
            get { return _inputDock; }
            set {
                if (_inputDock == value)
                    return;

                EditingPipe = null;
                _inputDock = value;

                if (_inputDock != null) {
                    _inputDock.node.moved += inputNodeMoved;
                    _inputDock.pipes.Add(this);
                    if (_inputDock.IsLoaded)
                        setAnchorPointB(inputDock.getPositionInCanvas());
                    else
                        _inputDock.Loaded += _inputDock_Loaded;
                }

                // 检查连线是否完成
                CheckIfPipeCompleted();
            }
        }

        private OutputDock _outputDock;
        public OutputDock outputDock {
            get { return _outputDock; }
            set {
                if (_outputDock == value)
                    return;

                EditingPipe = null;
                _outputDock = value;
                
                if (_outputDock != null) {
                    _outputDock.node.moved += outputNodeMoved;
                    _outputDock.pipes.Add(this);
                    if (_outputDock.IsLoaded)
                        setAnchorPointA(outputDock.getPositionInCanvas());
                    else
                        _outputDock.Loaded += _outputDock_Loaded;
                }

                // 检查连线是否完成
                CheckIfPipeCompleted();
            }
        }

        // 检查连线是否完成的方法
        public void CheckIfPipeCompleted() {
            if (_inputDock != null && _outputDock != null && hitTestPath != null) {
                // 连线完成后启用 HitTest
                hitTestPath.IsHitTestVisible = true;
            }
        }

        private void _outputDock_Loaded(object sender, RoutedEventArgs e) {
            _outputDock.Loaded -= _outputDock_Loaded;
            setAnchorPointA(outputDock.getPositionInCanvas());
        }

        private void _inputDock_Loaded(object sender, RoutedEventArgs e) {
            _inputDock.Loaded -= _inputDock_Loaded;
            setAnchorPointB(inputDock.getPositionInCanvas());
        }

        public Pipe(OutputDock output, InputDock input) {

            bezier = new BezierSegment() {
                IsStroked = true
            };

            // Set up the Path to insert the segments
            PathGeometry pathGeometry = new PathGeometry();

            pathFigure = new PathFigure();
            pathFigure.IsClosed = false;
            pathGeometry.Figures.Add(pathFigure);

            this.inputDock = input;
            this.outputDock = output;
            if (input == null || output == null)
                EditingPipe = this;

            pathFigure.Segments.Add(bezier);
            path = new Path();
            path.IsHitTestVisible = false;
            path.Stroke = graph.context.getPipeColor((input != null)? input.type : output.type);
            path.StrokeThickness = 2;
            path.Data = pathGeometry;

            // 添加透明的点击区域
            hitTestPath = new Path {
                Stroke =  Brushes.Transparent, // 透明的点击区域
                StrokeThickness = 6, // 扩大点击范围
                Data = pathGeometry, // 与实际线条共享几何数据
                IsHitTestVisible = false // 确保可以点击
            };

            path.Tag = this;
            // 将 path 关联到 hitTestPath 的 Tag 属性
            hitTestPath.Tag = path;

            // 将透明的 Path 添加到 Canvas 中
            graph.canvas.Children.Add(hitTestPath);
            graph.canvas.Children.Add(path);

            ((UIElement)graph.canvas.Parent).MouseMove += Canvas_MouseMove;
        }

        private void Canvas_MouseMove(object sender, MouseEventArgs e) {
            if (EditingPipe == null)
                ((UIElement)graph.canvas.Parent).MouseMove -= Canvas_MouseMove;

            if (outputDock == null) {
                setAnchorPointA(Mouse.GetPosition(graph.canvas));
            } else if (inputDock == null) {
                setAnchorPointB(Mouse.GetPosition(graph.canvas));
            }
        }

        private void setAnchorPointB(Point point) {
            bezier.Point3 = point;
            bezier.Point2 = new Point(bezier.Point3.X, bezier.Point3.Y - graph.pipeStiffness);
        }

        private void setAnchorPointA(Point point) {
            pathFigure.StartPoint = point;
            bezier.Point1 = new Point(pathFigure.StartPoint.X, pathFigure.StartPoint.Y + graph.pipeStiffness);
        }

        private void outputNodeMoved(Node node) {
            setAnchorPointA(outputDock.getPositionInCanvas());
        }

        private void inputNodeMoved(Node node) {
            setAnchorPointB(inputDock.getPositionInCanvas());
        }

        public void Dispose() {
            graph.canvas.Children.Remove(path);
            graph.canvas.Children.Remove(hitTestPath);
            
            if (graph.canvas.Parent is UIElement parent) {
                parent.MouseMove -= Canvas_MouseMove;
            }

            if (inputDock != null) {
                inputDock.pipes.Remove(this);
                inputDock.node.moved -= inputNodeMoved;
            }
            if (outputDock != null) {
                outputDock.pipes.Remove(this);
                outputDock.node.moved -= outputNodeMoved;
            }
        }
    }
}
